package com.excilys.blog.refactoring.refactored;

import java.util.ArrayList;
import java.util.List;

import com.excilys.blog.refactoring.GeoPoint;

abstract class PathFinder {

    private final GeoPointSet geoPointSet;

    private List<GeoPoint>    path;
    private GeoPoint          lastFoundPoint;

    private PathFinder(GeoPointSet geoPointSet) {
        this.geoPointSet = geoPointSet;
    }

    public List<GeoPoint> findPath(GeoPoint startPoint, GeoPoint endPoint) {

        initPath(startPoint);
        initLastFound(startPoint);
        findNextPoint();

        while (!endReached(endPoint)) {
            addBestCandidateToPath();
            findNextPoint();
        }

        return path;
    }

    private void initPath(GeoPoint startPoint) {
        path = new ArrayList<GeoPoint>();
        path.add(startPoint);
    }

    private void initLastFound(GeoPoint startPoint) {
        lastFoundPoint = startPoint;
    }

    private void findNextPoint() {
        PositionDelta bestDelta = null;
        GeoPoint bestPoint = null;
        for (GeoPoint point : geoPointSet) {
            PositionDelta delta = buildDelta(lastFoundPoint, point);
            if (delta.isBetterCandidate(bestDelta)) {
                bestPoint = point;
                bestDelta = delta;
            }
        }
        lastFoundPoint = bestPoint;
    }

    protected abstract PositionDelta buildDelta(GeoPoint currentPoint, GeoPoint point);

    private boolean endReached(GeoPoint endPoint) {
        return lastFoundPoint.equals(endPoint);
    }

    private void addBestCandidateToPath() {
        path.add(lastFoundPoint);
    }

    public static PathFinder westPathFinder(GeoPointSet geoPointSet) {
        return new PathFinder(geoPointSet) {
            @Override
            protected PositionDelta buildDelta(GeoPoint currentPoint, GeoPoint point) {
                return new WestPositionDelta(currentPoint, point);
            }
        };
    }

    public static PathFinder eastPathFinder(GeoPointSet geoPointSet) {
        return new PathFinder(geoPointSet) {
            @Override
            protected PositionDelta buildDelta(GeoPoint currentPoint, GeoPoint point) {
                return new EastPositionDelta(currentPoint, point);
            }
        };
    }

}
